package com.cpedia.android.deal.model;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Config;
import android.util.Log;
import com.cpedia.android.deal.Deal;
import com.cpedia.android.deal.Utility;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;

/**
 * <pre>
 * Implements a SQLite database backed local store for Deals.
 * </pre>
 */
public class LocalStore extends Store {
    /**
     * History of database revisions.
     * <p/>
     * db version   Shipped in  Notes
     * ----------   ----------  -----
     * 1            1.5         initial version.
     */

    private static final int DB_VERSION = 1;


    private String mPath;
    private SQLiteDatabase mDb;
    private Context mContext;

    /**
     * @param _uri local://localhost/path/to/database/uuid.db
     */
    public LocalStore(String _uri, Context context) throws MessagingException {
        mContext = context;
        URI uri = null;
        try {
            uri = new URI(_uri);
        } catch (Exception e) {
            throw new MessagingException("Invalid uri for LocalStore");
        }
        if (!uri.getScheme().equals("local")) {
            throw new MessagingException("Invalid scheme");
        }
        mPath = uri.getPath();

        File parentDir = new File(mPath).getParentFile();
        if (!parentDir.exists()) {
            parentDir.mkdirs();
        }
        mDb = SQLiteDatabase.openOrCreateDatabase(mPath, null);
        int oldVersion = mDb.getVersion();
        if (oldVersion != DB_VERSION) {
            if (Config.LOGV) {
                Log.v(Deal.LOG_TAG, String.format("Upgrading database from %d to %d",
                        oldVersion, DB_VERSION));
            }
            if (oldVersion < 1) {
                /**
                 * Missing or old:  Create up-to-date tables
                 */
                mDb.execSQL("DROP TABLE IF EXISTS deals");
                mDb.execSQL("CREATE TABLE deals (id INTEGER PRIMARY KEY, deal_id TEXT, vender TEXT, title TEXT, date INTEGER, " +
                        " flags TEXT,content TEXT, image_url TEXT "
                        + "last_updated INTEGER, internal_date INTEGER)");

                mDb.execSQL("DROP TABLE IF EXISTS coupons");
                mDb.execSQL("CREATE TABLE coupons (id INTEGER PRIMARY KEY, coupon_id TEXT, vender TEXT, flags TEXT," +
                        " code TEXT, discount TEXT, date INTEGER, flags TEXT, site_name TEXT, " +
                        "site_url TEXT, image_url TEXT, last_updated INTEGER, " +
                        "internal_date INTEGER)");

                mDb.execSQL("DROP TABLE IF EXISTS pending_commands");
                mDb.execSQL("CREATE TABLE pending_commands " +
                        "(id INTEGER PRIMARY KEY, command TEXT, arguments TEXT)");

                mDb.setVersion(DB_VERSION);
            }

            if (mDb.getVersion() != DB_VERSION) {
                throw new Error("Database upgrade failed!");
            }
        }

    }


    @Override
    public void checkSettings() throws MessagingException {
    }

    /**
     * Delete the entire Store and it's backing database.
     */
    public void delete() {
        try {
            mDb.close();
        } catch (Exception e) {

        }
        try {
            new File(mPath).delete();
        }
        catch (Exception e) {

        }
    }

    private void populateMessageFromGetMessageCursor(DealMessage message, Cursor cursor)
            throws MessagingException{
            message.setId(cursor.getInt(0));
            message.setDeal_id(cursor.getString(1));
            message.setVender(cursor.getString(2));
            message.setTitle(cursor.getString(3));
            message.setDate(new Date(cursor.getLong(4)));
            String flagList = cursor.getString(5);
            if (flagList != null && flagList.length() > 0) {
                String[] flags = flagList.split(",");
                try {
                    for (String flag : flags) {
                            message.mFlags.add(Flag.valueOf(flag.toUpperCase()));
                    }
                } catch (Exception e) {
                }
            }
            message.setContent(cursor.getString(6));
            message.setImage_url(cursor.getString(7));
            message.setLast_updated(new Date(cursor.getLong(8)));
            message.setInternal_date(new Date(cursor.getLong(9)));
    }


    @Override
    public DealMessage[] getDealMessages() throws MessagingException {
        ArrayList<DealMessage> messages = new ArrayList<DealMessage>();
        Cursor cursor = null;
        try {
            cursor = mDb.rawQuery(
                    "SELECT id, deal_id, vender, title, date, flags, content, "
                    + "image_url, last_updated, internal_date  "
                            + "FROM deals ", new String[] {});

            while (cursor.moveToNext()) {
                DealMessage message = new DealMessage();
                populateMessageFromGetMessageCursor(message, cursor);
                messages.add(message);
            }
        }
        finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        return messages.toArray(new DealMessage[] {});
    }

    public ArrayList<PendingCommand> getPendingCommands() {
        Cursor cursor = null;
        try {
            cursor = mDb.query("pending_commands",
                    new String[]{"id", "command", "arguments"},
                    null,
                    null,
                    null,
                    null,
                    "id ASC");
            ArrayList<PendingCommand> commands = new ArrayList<PendingCommand>();
            while (cursor.moveToNext()) {
                PendingCommand command = new PendingCommand();
                command.mId = cursor.getLong(0);
                command.command = cursor.getString(1);
                String arguments = cursor.getString(2);
                command.arguments = arguments.split(",");
                for (int i = 0; i < command.arguments.length; i++) {
                    command.arguments[i] = Utility.fastUrlDecode(command.arguments[i]);
                }
                commands.add(command);
            }
            return commands;
        }
        finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    public void addPendingCommand(PendingCommand command) {
        try {
            for (int i = 0; i < command.arguments.length; i++) {
                command.arguments[i] = URLEncoder.encode(command.arguments[i], "UTF-8");
            }
            ContentValues cv = new ContentValues();
            cv.put("command", command.command);
            cv.put("arguments", Utility.combine(command.arguments, ','));
            mDb.insert("pending_commands", "command", cv);
        }
        catch (UnsupportedEncodingException usee) {
            throw new Error("Aparently UTF-8 has been lost to the annals of history.");
        }
    }

    public void removePendingCommand(PendingCommand command) {
        mDb.delete("pending_commands", "id = ?", new String[]{Long.toString(command.mId)});
    }

    public static class PendingCommand {
        private long mId;
        public String command;
        public String[] arguments;

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            sb.append(command);
            sb.append("\n");
            for (String argument : arguments) {
                sb.append("  ");
                sb.append(argument);
                sb.append("\n");
            }
            return sb.toString();
        }
    }

}
